#include "camera_video_enc_imp.h"
#include "base_log.h"
#include "base_file_util.h"
#include <map>
#include <mutex>
#include <sys/time.h>
#include <thread>

#include "sample_venc.h"

namespace El {
namespace Hal {

static void video_stream_callback(int chn, ot_payload_type payload_type, ot_venc_stream *stStream)
{
    if (chn == 0) {
        return;
    }

    bool is_key_frame = false;
    uint32_t total_len = 0;
    for (uint32_t i = 0; i < stStream->pack_cnt; i++) {
        if ((stStream->pack[i].data_type.h265_type == OT_VENC_H265_NALU_IDR_SLICE && payload_type == OT_PT_H265) ||
            (stStream->pack[i].data_type.h264_type == OT_VENC_H264_NALU_IDR_SLICE && payload_type == OT_PT_H264)) {
            is_key_frame = true;
        }
        total_len += stStream->pack[i].len - stStream->pack[i].offset;
    }

    uint8_t *data = stStream->pack[0].addr + stStream->pack[0].offset;
    if (stStream->pack_cnt > 1) {
        data = (uint8_t *)malloc(total_len);
        if (data == NULL) {
            LOG_ERROR("malloc fail");
            return;
        }

        uint32_t write_len = 0;
        for (uint32_t i = 0; i < stStream->pack_cnt; i++) {
            if (memcpy_s(data + write_len, total_len - write_len, stStream->pack[i].addr + stStream->pack[i].offset,
                         stStream->pack[i].len - stStream->pack[i].offset) != EOK) {
                LOG_ERROR("memcpy_s failed");
            }
            write_len += stStream->pack[i].len;
        }
    }

    VideoEncPacket packet;
    memset(&packet, 0, sizeof(VideoEncPacket));
    packet.buf = data;
    packet.len = total_len;
    packet.pts = stStream->pack[0].pts;
    if (is_key_frame) {
        packet.frameType = MEDIA_FRAME_I;
    } else {
        packet.frameType = MEDIA_FRAME_P;
    }

    // 获取当前时间的微妙
    struct timeval tv;
    gettimeofday(&tv, NULL);
    packet.utc = (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec;
    packet.width = 1920;
    packet.height = 1080;
    packet.fps = 30;
    packet.packType = MEDIA_VIDEO_ENC_PACK_ES;
    if (payload_type == OT_PT_H264) {
        packet.codecType = MEDIA_VIDEO_CODEC_H264;
    } else if (payload_type == OT_PT_H265) {
        packet.codecType = MEDIA_VIDEO_CODEC_H265;
    } else {
        LOG_ERROR("unsupported payload type: {}", payload_type);
    }

    /// mpp中的通道号对应着应用层的码流类型
    int32_t stream_type = 0;
    if (chn == 0) {
        stream_type = MEDIA_VIDEO_ENC_STREAM_EXTRA1;
    } else if (chn == 1) {
        stream_type = MEDIA_VIDEO_ENC_STREAM_MAIN;
    } else {
        LOG_ERROR("unsupported chn: {}", chn);
        return;
    }

    ICameraVideoEnc &enc = ICameraVideoEnc::GetInstance(0, stream_type);
    CameraVideoEnc *videoEnc = static_cast<CameraVideoEnc *>(&enc);
    if (videoEnc != nullptr) {
        videoEnc->OnStream(packet);
    }

    if (stStream->pack_cnt > 1) {
        free(data);
    }
}

ICameraVideoEnc &ICameraVideoEnc::GetInstance(uint32_t channel, uint32_t stream)
{
    static std::map<std::pair<uint32_t, uint32_t>, CameraVideoEnc *> instances;
    static std::mutex mutex;

    std::lock_guard<std::mutex> lock(mutex);
    auto key = std::make_pair(channel, stream);
    auto it = instances.find(key);
    if (it == instances.end()) {
        CameraVideoEnc *instance = new CameraVideoEnc(channel, stream);
        instances.emplace(key, instance);
        return *instance;
    }
    return *it->second;
}

CameraVideoEnc::CameraVideoEnc(uint32_t channel, uint32_t stream) : channel_(channel), stream_(stream) {}

bool CameraVideoEnc::init_{false};
int32_t CameraVideoEnc::Start(PacketCallback cb)
{
    if (!init_) {
        set_callback(video_stream_callback);
        std::thread([]() { sample_venc_normal(); }).detach();
        init_ = true;
    }

    int32_t handle = 0;
    handle = cb_list_.Register(cb);
    if (handle < 0) {
        LOG_ERROR("Failed to register callback");
        return -1;
    }
    return handle;
}

void CameraVideoEnc::Stop(int32_t handle)
{
    cb_list_.Unregister(handle);
}

void CameraVideoEnc::OnStream(VideoEncPacket &packet)
{
    cb_list_.InvokeAll(packet);
}

bool CameraVideoEnc::ForceIFrame()
{
    int32_t ret = 0;
    if (stream_ == MEDIA_VIDEO_ENC_STREAM_MAIN) {
        ret = hi_mpi_venc_request_idr(1, HI_TRUE); // 主码流使用通道1,立即生效
    } else if (stream_ == MEDIA_VIDEO_ENC_STREAM_EXTRA1) {
        ret = hi_mpi_venc_request_idr(0, HI_TRUE); // 子码流使用通道0,立即生效
    }
    return ret == 0;
}

bool CameraVideoEnc::Snapshot()
{
    return true;
}

bool CameraVideoEnc::GetEncFormat(VideoEncFormat &enc)
{
    enc.codecType = MEDIA_VIDEO_CODEC_H264;
    enc.width = 1920;
    enc.height = 1080;
    enc.fps = 30;
    return true;
}

void CameraVideoEnc::SetOSD(bool enable)
{
    (void)enable;
}

} // namespace Hal
} // namespace El